package com.cin.cma.client.service.impl;


import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.date.DateField;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson2.JSONObject;
import com.cin.cma.client.config.OpenClient;
import com.cin.cma.client.config.OpenConfig;
import com.cin.cma.client.model.DataCinema;
import com.cin.cma.client.model.FinixxSettings;
import com.cin.cma.client.model.finixx.Extend;
import com.cin.cma.client.model.finixx.cinema.Cinema;
import com.cin.cma.client.model.finixx.cinema.Hall;
import com.cin.cma.client.model.finixx.schedule.Schedule;
import com.cin.cma.client.model.finixx.seats.Seat;
import com.cin.cma.client.service.IConnectionService;
import com.cin.cma.client.service.IFinixxProxyService;
import com.cin.cma.client.service.IFinixxPushService;
import com.cin.cma.common.constant.MsgTypeConstant;
import com.cin.cma.common.enums.*;
import com.cin.cma.common.model.ApiResponse;
import com.cin.cma.common.model.SocketRequest;
import com.cin.cma.common.model.SocketResponse;
import com.cin.cma.common.model.finixx.base.BaseRequest;
import com.cin.cma.common.model.finixx.request.IncreaseBookingSettingRequest;
import com.cin.cma.common.model.finixx.request.SectionsRequest;
import com.cin.cma.common.model.finixx.request.SessionRequest;
import com.cin.cma.common.model.finixx.response.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.util.*;

@Slf4j
@Service
public class FinixxPushServiceImpl implements IFinixxPushService {
    /**
     * 获取服务端配置信息
     */
    private static final String SETTINGS_URL = "/admin-api/push/finixx/settings/get";
    /**
     * 推送影院信息
     */
    private static final String CINEMA_URI = "/admin-api/push/cinema";
    /**
     * 推送排期信息
     */
    private static final String SCHEDULE_URI = "/admin-api/push/schedule";
    /**
     * 推送座位图信息
     */
    private static final String SEATPLAN_URI = "/admin-api/push/seatPlan";
    @Autowired
    private OpenConfig openConfig;
    @Autowired
    private IFinixxProxyService finixxProxyService;
    @Autowired
    private IConnectionService connectionService;

    /**
     * 获取基础请求实体
     *
     * @return BaseRequest
     * @throws Exception 异常
     */
    private FinixxSettings getBaseSettings() throws Exception {
        ApiResponse<?> apiResponse = OpenClient.builder().config(openConfig).build().execute(SETTINGS_URL, null);
        if (!apiResponse.getSuccess()) {
            throw new Exception(apiResponse.getError().getCode() + "::" + apiResponse.getError().getMessage());
        }
        return ((JSONObject) apiResponse.getData()).toJavaObject(FinixxSettings.class);
    }

    /**
     * 影院信息
     */
    @Override
    public void cinema() throws Exception {
        DataCinema dataCinema = this.connectionService.getLocalCinema();
        FinixxSettings finixxSettings = this.getBaseSettings();
        BaseRequest baseRequest = BeanUtil.copyProperties(finixxSettings, BaseRequest.class);
        baseRequest.setLocationCd(dataCinema.getCinemaLinkId());
        baseRequest.setCode("0104");
        SocketRequest socketRequest = new SocketRequest();
        socketRequest.setRequestId(baseRequest.getSerialNumber());
        socketRequest.setRequestBody(JSONObject.toJSONString(baseRequest));
        socketRequest.setMessageType(MsgTypeConstant.SYNC);
        //影院信息
        SocketResponse cinemaResponse = this.finixxProxyService.request("api/basic/getlocalcinema", JSONObject.toJSONString(socketRequest));
        if (cinemaResponse.getHttpStatus() != 200) {
            throw new Exception(baseRequest.getCode() + "网络请求失败：" + cinemaResponse.getHttpStatus());
        }
        //影厅列表
        baseRequest.setCode("1006");
        socketRequest.setRequestBody(JSONObject.toJSONString(baseRequest));
        SocketResponse hallResponse = this.finixxProxyService.request("api/ticket/halls", JSONObject.toJSONString(socketRequest));
        if (hallResponse.getHttpStatus() != 200) {
            throw new Exception(baseRequest.getCode() + "网络请求失败：" + hallResponse.getHttpStatus());
        }
        //渠道信息
        IncreaseBookingSettingRequest request = BeanUtil.copyProperties(baseRequest, IncreaseBookingSettingRequest.class);
        request.setCode("0419");
        request.setChannelCd(finixxSettings.getChannelCd());
        socketRequest.setRequestBody(JSONObject.toJSONString(request));
        SocketResponse channelResponse = this.finixxProxyService.request("api/basic/getincreasebookingsetting", JSONObject.toJSONString(socketRequest));
        if (channelResponse.getHttpStatus() != 200) {
            throw new Exception(request.getCode() + "网络请求失败：" + channelResponse.getHttpStatus());
        }
        IncreaseBookingSettingResponse increaseBookingSettingResponse = JSONObject.parseObject(channelResponse.getResponseBody(), IncreaseBookingSettingResponse.class);
        if (increaseBookingSettingResponse.getResult() != 0) {
            throw new Exception(request.getCode() + "获取渠道信息失败：" + increaseBookingSettingResponse.getResult() + "::" + increaseBookingSettingResponse.getMessage());
        }
        HallsResponse hallsResponse = JSONObject.parseObject(hallResponse.getResponseBody(), HallsResponse.class);
        if (hallsResponse.getResult() != 0) {
            throw new Exception(request.getCode() + "获取影厅信息失败：" + hallsResponse.getResult() + "::" + hallsResponse.getMessage());
        }
        LocalCinemaResponse localCinemaResponse = JSONObject.parseObject(cinemaResponse.getResponseBody(), LocalCinemaResponse.class);
        if (localCinemaResponse.getResult() != 0) {
            throw new Exception(request.getCode() + "获取影院信息失败：" + localCinemaResponse.getResult() + "::" + localCinemaResponse.getMessage());
        }
        Cinema result = new Cinema();
        result.setCinemaCode(localCinemaResponse.getLocationCode());
        result.setCinemaLinkId(localCinemaResponse.getId());
        result.setCinemaName(localCinemaResponse.getName());
        result.setCityCode(null);
        result.setCityName(null);
        result.setHallCount(hallsResponse.getHalls().size());
        result.setRefundBeforeTime("0");
        result.setRefundLimitCount("9999");
        Optional<IbtListItemResponse> ibtModel = increaseBookingSettingResponse.getIbtList().stream().filter(v -> v.getCd().equals(request.getChannelCd())).findFirst();
        if (!ibtModel.isPresent()) {
            throw new Exception(request.getCode() + "获取渠道信息失败：" + request.getChannelCd() + "不存在");
        }
        result.setMaxScheduleDay(ibtModel.get().getSaleBookingDays());
        result.setMaxTicketNum(ibtModel.get().getSaleMaxTickets());
        result.setMaxGoodsNum(9999);
        result.setMonthlyTicketRefund(-1);
        result.setCinemaAddress(localCinemaResponse.getAddress1());
        result.setRefundSpecialScheduleList(null);
        result.setHallList(new ArrayList<>());
        for (HallNodeResponse hall : hallsResponse.getHalls()) {
            Hall hallItem = new Hall();
            hallItem.setHallCode(hall.getCinemaCd());
            hallItem.setHallName(hall.getCinemaName());
            hallItem.setSeatCount(hall.getSeatsCount());
            hallItem.setHallType(HallTypeEnum.buildHallType(hall.getCinemaType()));
            hallItem.setExtendInfo(null);
            result.getHallList().add(hallItem);
        }
        Extend extend = new Extend();
        extend.setChannleType(InterfaceChannelEnum.FINIXX.getCode());
        extend.setDataType(DataTypeEnum.CINEMA);
        extend.setData(new HashMap<>());
        extend.getData().put("cinema", localCinemaResponse);
        extend.getData().put("hall", hallsResponse);
        extend.getData().put("channel", increaseBookingSettingResponse);
        result.setExtendInfo(extend);
        ApiResponse<?> pushResponse = OpenClient.builder().config(openConfig).build().execute(CINEMA_URI, result);
        if (pushResponse.getSuccess()) {
            log.info("推送影院信息成功！");
            return;
        }
        log.error("推送影院信息失败！error={}", pushResponse.getError());
    }

    /**
     * 排期
     */
    @Override
    public void schedules() throws Exception {
        DataCinema dataCinema = this.connectionService.getLocalCinema();
        FinixxSettings finixxSettings = this.getBaseSettings();
        //渠道信息
        IncreaseBookingSettingRequest request = BeanUtil.copyProperties(finixxSettings, IncreaseBookingSettingRequest.class);
        request.setLocationCd(dataCinema.getCinemaLinkId());
        request.setCode("0419");
        request.setChannelCd(finixxSettings.getChannelCd());
        SocketRequest socketRequest = new SocketRequest();
        socketRequest.setRequestId(request.getSerialNumber());
        socketRequest.setRequestBody(JSONObject.toJSONString(request));
        socketRequest.setMessageType(MsgTypeConstant.SYNC);
        SocketResponse channelResponse = this.finixxProxyService.request("api/basic/getincreasebookingsetting", JSONObject.toJSONString(socketRequest));
        if (channelResponse.getHttpStatus() != 200) {
            throw new Exception(request.getCode() + "网络请求失败：" + channelResponse.getHttpStatus());
        }
        IncreaseBookingSettingResponse increaseBookingSettingResponse = JSONObject.parseObject(channelResponse.getResponseBody(), IncreaseBookingSettingResponse.class);
        if (increaseBookingSettingResponse.getResult() != 0) {
            throw new Exception(request.getCode() + "获取渠道信息失败：" + increaseBookingSettingResponse.getResult() + "::" + increaseBookingSettingResponse.getMessage());
        }
        Optional<IbtListItemResponse> ibtModel = increaseBookingSettingResponse.getIbtList().stream().filter(v -> v.getCd().equals(request.getChannelCd())).findFirst();
        if (!ibtModel.isPresent()) {
            throw new Exception(request.getCode() + "获取渠道信息失败：" + request.getChannelCd() + "不存在");
        }
        SessionRequest baseRequest = BeanUtil.copyProperties(finixxSettings, SessionRequest.class);
        baseRequest.setSystemCD(finixxSettings.getChannelCd());
        baseRequest.setLocationCd(dataCinema.getCinemaLinkId());
        for (int i = 0; i < ibtModel.get().getSaleBookingDays(); i++) {
            try {
                String showDate = DateTime.now().offsetNew(DateField.DAY_OF_MONTH, i).toString("yyyy-MM-dd");
                baseRequest.setShowDate(showDate);
                baseRequest.setCode("1004");
                socketRequest.setRequestBody(JSONObject.toJSONString(baseRequest));
                SocketResponse socketResponse = this.finixxProxyService.request("api/ticket/session", JSONObject.toJSONString(socketRequest));
                if (socketResponse.getHttpStatus() != 200) {
                    throw new Exception(baseRequest.getCode() + "网络请求失败：" + socketResponse.getHttpStatus());
                }
                SessionResponse sessionResponse = JSONObject.parseObject(socketResponse.getResponseBody(), SessionResponse.class);
                if (sessionResponse.getResult() != 0) {
                    throw new Exception(baseRequest.getCode() + "获取排期信息失败：" + sessionResponse.getResult() + "::" + sessionResponse.getMessage());
                }
                List<Schedule> schedules = new ArrayList<>();
                for (SessionsItemResponse session : sessionResponse.getSessions()) {
                    Schedule schedule = new Schedule();
                    schedule.setCinemaCode(dataCinema.getCinemaCode());
                    schedule.setCinemaLinkId(dataCinema.getCinemaLinkId());
                    schedule.setHallCode(session.getCinemaCd());
                    schedule.setHallName(session.getCinemaName());
                    schedule.setCloudScheduleCode(session.getRefSeqNo());
                    schedule.setClocalScheduleCode(session.getRefSeqNo());
                    schedule.setScheduleKey(session.getFilmCd());
                    schedule.setSessionCode(session.getSessionCode());
                    String taskStartStr = String.format("%04d", session.getShowTimeInt());
                    String startTime = taskStartStr.substring(0, 2) + ":" + taskStartStr.substring(2) + ":00";
                    schedule.setShowDateTime(DateTime.of(session.getShowDate() + " " + startTime, "yyyy-MM-dd HH:mm"));
                    schedule.setStandardPrice(session.getMarketPrice());
                    schedule.setLowestPrice(session.getLeastPrice());
                    schedule.setStopSellTime(DateTime.of(schedule.getShowDateTime()).offsetNew(DateField.MINUTE, ibtModel.get().getSaleDelayTime()));
                    schedule.setInterfaceServiceFee(new BigDecimal("0"));
                    schedule.setInterfaceWebFee(new BigDecimal("0"));
                    schedule.setHallServiceFee(new BigDecimal("0"));
                    schedule.setHallServiceFeeType(ServiceFeeEnum.TICKET);
                    schedule.setSettlePrice(session.getMarketPrice());
                    schedule.setStatus(session.getSaleOpen() ? ScheduleStatusEnum.SELL_OPEN : ScheduleStatusEnum.SELL_STOP);
                    schedule.setFilmCode(session.getFilmCode());
                    schedule.setFilmName(session.getFilmLongTitle());
                    schedule.setShowType(ShowTypeEnum.buildShowTypeEnum(session.getDimensional()));
                    schedule.setLanguage(session.getLanguageName());
                    schedule.setDuration(session.getFilmDuration());
                    schedule.setAreaFeeList(null);
                    Extend extend = new Extend();
                    extend.setChannleType(InterfaceChannelEnum.FINIXX.getCode());
                    extend.setDataType(DataTypeEnum.SCHEDULE);
                    extend.setData(BeanUtil.beanToMap(session));
                    schedule.setExtendInfo(extend);
                    schedules.add(schedule);
                }
                JSONObject result = new JSONObject();
                result.put("data", schedules);
                result.put("cinemaCode", dataCinema.getCinemaCode());
                result.put("cinemaName", dataCinema.getCinemaName());
                result.put("scheduleDate", showDate);
                ApiResponse<?> pushResponse = OpenClient.builder().config(openConfig).build().execute(SCHEDULE_URI, result);
                if (pushResponse.getSuccess()) {
                    log.info("推送排期信息成功！showDate={}", baseRequest.getShowDate());
                    continue;
                }
                throw new Exception(baseRequest.getCode() + "推送影排期信息失败：" + pushResponse.getError().getCode() + "::" + pushResponse.getError().getMessage());
            } catch (Exception e) {
                log.error("推送排期信息失败！showDate={},error={}", baseRequest.getShowDate(), e.getMessage());
            }
        }
    }

    /**
     * 座位图（默认推送全部）
     */
    @Override
    public void seatPlan() throws Exception {
        DataCinema dataCinema = this.connectionService.getLocalCinema();
        FinixxSettings finixxSettings = this.getBaseSettings();
        BaseRequest baseRequest = BeanUtil.copyProperties(finixxSettings, BaseRequest.class);
        baseRequest.setLocationCd(dataCinema.getCinemaLinkId());
        //影厅列表
        baseRequest.setCode("1006");
        SocketRequest socketRequest = new SocketRequest();
        socketRequest.setRequestId(baseRequest.getSerialNumber());
        socketRequest.setMessageType(MsgTypeConstant.SYNC);
        socketRequest.setRequestBody(JSONObject.toJSONString(baseRequest));
        SocketResponse hallResponse = this.finixxProxyService.request("api/ticket/halls", JSONObject.toJSONString(socketRequest));
        if (hallResponse.getHttpStatus() != 200) {
            throw new Exception(baseRequest.getCode() + "网络请求失败：" + hallResponse.getHttpStatus());
        }
        HallsResponse hallsResponse = JSONObject.parseObject(hallResponse.getResponseBody(), HallsResponse.class);
        if (hallsResponse.getResult() != 0) {
            throw new Exception(baseRequest.getCode() + "获取影厅信息失败：" + hallsResponse.getResult() + "::" + hallsResponse.getMessage());
        }

        for (HallNodeResponse hall : hallsResponse.getHalls()) {
            try {
                //获取场区
                SectionsRequest sectionsRequest = BeanUtil.copyProperties(baseRequest, SectionsRequest.class);
                sectionsRequest.setShowDate(DateTime.now().toDateStr());
                sectionsRequest.setCode("1008");
                sectionsRequest.setCinemaCd(hall.getCinemaCd());
                socketRequest.setRequestBody(JSONObject.toJSONString(sectionsRequest));
                SocketResponse sectionSResponse = this.finixxProxyService.request("api/ticket/sections", JSONObject.toJSONString(socketRequest));
                if (sectionSResponse.getHttpStatus() != 200) {
                    throw new Exception(baseRequest.getCode() + "网络请求失败：" + hallResponse.getHttpStatus());
                }
                SectionsResponse sectionsResponse = JSONObject.parseObject(sectionSResponse.getResponseBody(), SectionsResponse.class);
                if (sectionsResponse.getResult() != 0) {
                    throw new Exception(baseRequest.getCode() + "获取场区信息失败：" + sectionsResponse.getResult() + "::" + sectionsResponse.getMessage());
                }
                //获取座位图
                sectionsRequest.setCode("1009");
                sectionsRequest.setCinemaCd(hall.getCinemaCd());
                socketRequest.setRequestBody(JSONObject.toJSONString(sectionsRequest));
                SocketResponse seatResponse = this.finixxProxyService.request("api/ticket/seatplan", JSONObject.toJSONString(socketRequest));
                if (seatResponse.getHttpStatus() != 200) {
                    throw new Exception(baseRequest.getCode() + "网络请求失败：" + seatResponse.getHttpStatus());
                }
                SeatPlanResponse seatPlanResponse = JSONObject.parseObject(seatResponse.getResponseBody(), SeatPlanResponse.class);
                if (seatPlanResponse.getResult() != 0) {
                    throw new Exception(baseRequest.getCode() + "获取座位图信息失败：" + seatPlanResponse.getResult() + "::" + seatPlanResponse.getMessage());
                }
                List<Seat> result = new ArrayList<>();
                int scheduleDate = Convert.toInt(sectionsRequest.getShowDate().replace("-", ""));
                Map<String, SeatPlansItemResponse> seatMap = new HashMap<>();
                for (SeatPlansItemResponse seatPlan : seatPlanResponse.getSeatPlans()) {
                    int effectDate = Convert.toInt(DateTime.of(seatPlan.getEffectiveDate()).toString("yyyyMMdd"));
                    if (effectDate > scheduleDate) {
                        result.addAll(this.buildSeat(dataCinema.getCinemaCode(), dataCinema.getCinemaLinkId(), seatPlan, sectionsResponse.getSections()));
                    } else {
                        SeatPlansItemResponse seatItem = seatMap.get(seatPlan.getSectionId());
                        if (seatItem == null || seatPlan.getEffectiveDate().getTime() > seatItem.getEffectiveDate().getTime()) {
                            seatMap.put(seatPlan.getSectionId(), seatPlan);
                        }
                    }
                }
                seatMap.forEach((k, v) -> result.addAll(this.buildSeat(dataCinema.getCinemaCode(), dataCinema.getCinemaLinkId(), v, sectionsResponse.getSections())));

                //推送座位图
                JSONObject data = new JSONObject();
                data.put("data", result);
                data.put("cinemaCode", dataCinema.getCinemaCode());
                data.put("cinemaName", dataCinema.getCinemaName());
                data.put("hallName", hall.getCinemaName());
                data.put("hallCode", hall.getCinemaCd());
                ApiResponse<?> pushResponse = OpenClient.builder().config(openConfig).build().execute(SEATPLAN_URI, data);
                if (pushResponse.getSuccess()) {
                    log.info("推送座位图信息成功！hallCode={}", hall.getCinemaCd());
                    continue;
                }
                throw new Exception(baseRequest.getCode() + "推送座位图信息失败：" + pushResponse.getError().getCode() + "::" + pushResponse.getError().getMessage());
            } catch (Exception e) {
                log.error("推送排期信息失败！hallCode={},error={}", hall.getCinemaCd(), e.getMessage());
            }
        }
    }

    /**
     * 构建座位
     *
     * @param cinemaCode   影院八位
     * @param cinemaLinkId 影院内码
     * @param seatPlan     座位图
     * @return List<SeatResponseBO>
     */
    private List<Seat> buildSeat(String cinemaCode, String cinemaLinkId, SeatPlansItemResponse seatPlan, List<SectionsItemResponse> sections) {
        List<Seat> result = new ArrayList<>();
        for (SeatsMsgItemResponse seat : seatPlan.getSeats()) {
            Seat seatResponse = new Seat();
            seatResponse.setCinemaCode(cinemaCode);
            seatResponse.setCinemaLinkId(cinemaLinkId);
            seatResponse.setSeatCode(StrUtil.format("{}-{}-{}", seat.getSeatCode(), seat.getRowId(), seat.getColId()));
            seatResponse.setRow(seat.getRowId());
            seatResponse.setCol(seat.getColId());
            seatResponse.setX(seat.getColNum());
            seatResponse.setY(seat.getRowNum());
            switch (seat.getSeatType()) {
                //情侣座
                case "L":
                case "R":
                    seatResponse.setSeatType(SeatTypeEnum.LOVE);
                    String loveCode = null;
                    if ("L".equalsIgnoreCase(seat.getSeatType())) {
                        //循环10次有空列，大于10次不处理！性能损耗过大~
                        for (int i = 1; i <= 10; i++) {
                            Integer colNumber = seat.getColNum() + i;
                            Optional<SeatsMsgItemResponse> seatOptional = seatPlan.getSeats().stream().filter(v -> v.getRowNum().equals(seat.getRowNum()) && v.getColNum().equals(colNumber)).findFirst();
                            if (!seatOptional.isPresent()) {
                                continue;
                            }
                            if ("R".equals(seatOptional.get().getSeatType())) {
                                loveCode = "" + seat.getRowNum() + seat.getColNum() + seatOptional.get().getRowNum() + seatOptional.get().getColNum();
                            }
                            break;
                        }
                    } else if ("R".equalsIgnoreCase(seat.getSeatType())) {
                        //循环10次有空列，大于10次不处理！性能损耗过大~
                        for (int i = 1; i <= 10; i++) {
                            int colNumber = seat.getColNum() - i;
                            if (colNumber <= 0) {
                                continue;
                            }
                            Optional<SeatsMsgItemResponse> seatOptional = seatPlan.getSeats().stream().filter(v -> v.getRowNum().equals(seat.getRowNum()) && v.getColNum().equals(colNumber)).findFirst();
                            if (!seatOptional.isPresent()) {
                                continue;
                            }
                            if ("L".equals(seatOptional.get().getSeatType())) {
                                loveCode = "" + seatOptional.get().getRowNum() + seatOptional.get().getColNum() + seat.getRowNum() + seat.getColNum();
                            }
                            break;
                        }
                    }
                    seatResponse.setLoveCode(loveCode);
                    break;
                //包厢
                case "B":
                    seatResponse.setSeatType(SeatTypeEnum.BOX);
                    break;
                //保留
                case "H":
                    seatResponse.setSeatType(SeatTypeEnum.RESERVE);
                    break;
                //残疾座位
                case "W":
                    seatResponse.setSeatType(SeatTypeEnum.DISABILITY);
                    break;
                //按摩椅
                case "Z":
                    seatResponse.setSeatType(SeatTypeEnum.MASSAGE_CHAIR);
                    break;
                //普通座
                default:
                    seatResponse.setSeatType(SeatTypeEnum.NORMAL);
                    break;
            }
            seatResponse.setSectionCode(seatPlan.getSectionId());
            sections.stream().filter(v -> v.getSectionID().equals(seatPlan.getSectionId())).findFirst()
                    .ifPresent(v -> seatResponse.setSectionName(v.getSectionName()));
            seatResponse.setSeatStatus(seat.getSeatIsDamaged() ? SeatStatusEnum.UNAVAILABLE : SeatStatusEnum.UNSOLD);
            seatResponse.setEffectiveDate(seatPlan.getEffectiveDate());
            Extend extend = new Extend();
            extend.setChannleType(InterfaceChannelEnum.FINIXX.getCode());
            extend.setDataType(DataTypeEnum.SEAT);
            extend.setData(BeanUtil.beanToMap(seat));
            seatResponse.setExtendInfo(extend);
            result.add(seatResponse);
        }
        return result;
    }
}
